Last Updated: December 19, 2025
Cricinfo (officially ESPNcricinfo) is a comprehensive online platform dedicated to cricket. It provides live scores, ball-by-ball commentary, match schedules, team and player statistics, and news related to cricket matches.
In this chapter, we will explore the low-level design of cricinfo like service.
Let's start by clarifying the requirements:
Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions, clarify ambiguities, and define the system's scope more precisely.
Here is an example of how a conversation between the candidate and the interviewer might unfold:
Candidate: Should we support different match formats like ODI, T20, and Test?
Interviewer: Yes, the system should support multiple match formats.
Candidate: Should the system just show the overall score, or should it provide detailed ball-by-ball commentary?
Interviewer: It should be detailed. Users should be able to follow a match ball-by-ball.
Candidate: How does this live data get into our system? Should we design an admin interface to input match data, or can we assume we are consuming a feed from a third-party provider?
Interviewer: Let's assume there is a reliable, official third-party API that pushes ball-by-ball data to our system. For this version, we can hardcode a series of match events.
Candidate: Should the system maintain historical data like past matches, player stats, and records?
Interviewer: Yes, users should be able to browse historical data for players, teams, and matches.
Candidate: Should the system display commentary and real-time score updates?
Interviewer: Yes, real-time updates and ball-by-ball commentary are essential features of the system.
Candidate: Should the system allow tracking multiple matches simultaneously?
Interviewer: Yes, the system should support concurrent matches.
Candidate: Should users be able to receive notifications for key match events like wickets, boundaries, or player milestones?
Interviewer: Yes, the system should allow users to subscribe to a match and receive notifications for important events.
After gathering the details, we can summarize the key system requirements.
Core entities are the fundamental building blocks of our system. We identify them by analyzing key nouns (e.g., match, team, player, venue, series, scorecard, commentary) and actions (e.g., list, display, follow, update) from the functional requirements. These typically translate into classes, enums, or interfaces in an object-oriented design.
Let’s walk through the functional requirements and extract the relevant entities:
This immediately points to a central Match entity. A Match will encapsulate all details of a single game, including the participating teams, venue, and start time. To handle different formats, we can use a MatchFormat enum (TEST, ODI, T20) and a corresponding MatchFormatStrategy interface to encapsulate format-specific rules (e.g., overs per innings). To manage the state of a game, a MatchStatus enum (UPCOMING, LIVE, COMPLETED) is essential.
This fine-grained requirement necessitates a Ball entity to represent a single delivery. Each Ball object will capture who bowled it, who faced it, the runs scored, and any extras. If a dismissal occurs, a Wicket entity can be associated with the Ball to detail how the player got out. Since a match is divided into turns, an Inning entity is needed to group all the balls bowled by one team and track the batting team's total score and wickets.
To present the match data, we need several entities. A Scorecard entity will aggregate the statistics for each Inning, including batting and bowling figures. For live text updates, a Commentary entity is needed to hold the descriptive text for each Ball. Individual player performance within a match can be tracked using a PlayerStats object.
The system needs to model the core participants. This leads to Player and Team entities. A Player will have attributes like name and role, while a Team will be a collection of players. To support historical queries, a StatsService or repository layer is needed to retrieve past match data and career statistics.
To manage the creation of matches, processing of ball-by-ball data, and subscriptions, a high-level facade is useful. A CricInfoService entity can serve as this single entry point, simplifying interactions for the client and hiding the system's internal complexity.
Enums (MatchType, MatchStatus, PlayerRole, WicketType): Define fixed sets of constants for consistency and type safety throughout the system.Match: Represents a single cricket match with format, teams, and timing.Team: Represents a cricket team.Player: Represents an individual cricketer with personal details, a role (batsman, bowler), and associated statistics.Inning: Represents one team's batting turn within a match. It contains a log of all balls bowled and aggregates the total score and wickets.Ball: Represents a single delivery in a match, capturing all outcomes like runs scored, extras, and any Wicket.Wicket: Describes a dismissal event, including the type of dismissal (e.g., Bowled, Caught) and the players involved.Scorecard: Contains statistical details of a match, broken down by team and inning.Commentary: Stores the descriptive text associated with a single Ball, used for providing live updates.CricInfoService: A Facade and Singleton that provides a simplified, high-level API for interacting with the entire system.These core entities define the essential abstractions of a CricInfo-like system and will guide the structure of your low-level design and class diagrams.
This section breaks down the system's architecture into its fundamental classes, their responsibilities, and the relationships that connect them. We also explore the key design patterns that provide robustness and flexibility to the solution.
The system is composed of several types of classes, each with a distinct role.
MatchType: Defines the format of the match (e.g., T20, ODI).MatchStatus: Represents the real-time status of a match (SCHEDULED, LIVE, FINISHED).PlayerRole: Classifies players based on their primary skill (BATSMAN, BOWLER).WicketType: Specifies the method of dismissal (BOWLED, CAUGHT).ExtraType: Denotes types of extra runs (WIDE, NO_BALL).PlayerRepresents a cricket player, containing their ID, name, role, and an associated PlayerStats object.
PlayerStatsA mutable data class that tracks a player's performance within a match (runs scored, wickets taken, etc.).
TeamRepresents a cricket team, containing its ID, name, and a list of Players.
WicketA data class capturing the details of a dismissal, including the type, player out, and other involved players.
Its construction is managed by the Builder pattern.
BallRepresents a single delivery in an innings.
It encapsulates all outcomes of that delivery: runs, extras, and any wicket that fell. It is also constructed using the Builder pattern.
InningsManages the entire course of one team's batting turn.
It contains the list of all balls bowled, tracks the team's score and wickets, and maintains the individual statistics for all players involved.
MatchThe central engine of the system.
It acts as the Context for the State pattern (delegating actions to its currentState object) and the Subject for the Observer pattern (notifying all subscribed observers of updates).
CommentaryManagerA centralized service that dynamically generates descriptive text commentary for each ball based on its outcome, using a template-based approach.
MatchRepository, PlayerRepositorySimple data access classes that simulate a persistence layer for storing and retrieving match and player data.
CricInfoService (Singleton & Facade)The primary entry point for the application.
It simplifies all client interactions by providing a high-level API for creating matches, processing ball updates, and subscribing observers, hiding the complex internal workings.
The relationships between classes define the system's structure and data flow.
Team is composed of a list of Players.Match is composed of one or more Innings.Innings is composed of a list of Balls and manages PlayerStats.Player has their own PlayerStats.Match is associated with two Teams and one MatchFormatStrategy.Match has a current MatchState.Match (Subject) is associated with a list of MatchObservers.Ball is associated with the Player who bowled it and the Player who faced it.MatchState classes implement the MatchState interface.MatchObserver classes implement the MatchObserver interface.MatchFormatStrategy classes implement the MatchFormatStrategy interface.CricInfoService facade depends on Match, Player, and the repositories to orchestrate the application.Ball.BallBuilder depends on the CommentaryManager to generate commentary during its construction.LiveState depends on Innings to process ball updates and check for end-of-match conditions.The MatchFormatStrategy allows the rules of a match (T20, ODI, etc.) to be encapsulated in separate objects. This makes the system flexible and easy to extend with new cricket formats without modifying the core Match logic.
The lifecycle of a Match is managed using the State pattern. The Match (Context) delegates its behavior to different MatchState objects (LiveState, InBreakState, FinishedState). This cleanly separates state-specific logic and makes managing transitions robust and easy to understand.
This pattern is fundamental for providing live updates. The Match (Subject) notifies all registered MatchObservers (ScorecardDisplay, CommentaryDisplay, etc.) after every ball. This decouples the core match engine from the various ways data can be presented to the user.
Used for the step-by-step construction of complex objects like Ball and Wicket. It provides a fluent API, handles optional parameters gracefully (e.g., a Wicket may or may not have a caughtBy player), and centralizes the creation logic.
The CricInfoService class serves as a facade. It provides a simple, high-level API (createMatch, processBallUpdate, subscribeToMatch) that hides the complex internal workflows involving states, strategies, observers, and repositories.
CricInfoService and CommentaryManager are implemented as singletons. This ensures a single, globally accessible point of control for managing the application's flow and for generating commentary, preventing inconsistent states and resource duplication.
1class MatchType(Enum):
2 T20 = "T20"
3 ODI = "ODI"
4 TEST = "TEST"
5
6class MatchStatus(Enum):
7 SCHEDULED = "SCHEDULED"
8 LIVE = "LIVE"
9 IN_BREAK = "IN_BREAK"
10 FINISHED = "FINISHED"
11 ABANDONED = "ABANDONED"
12
13class PlayerRole(Enum):
14 BATSMAN = "BATSMAN"
15 BOWLER = "BOWLER"
16 ALL_ROUNDER = "ALL_ROUNDER"
17 WICKET_KEEPER = "WICKET_KEEPER"
18
19class WicketType(Enum):
20 BOWLED = "BOWLED"
21 CAUGHT = "CAUGHT"
22 LBW = "LBW"
23 RUN_OUT = "RUN_OUT"
24 STUMPED = "STUMPED"
25 HIT_WICKET = "HIT_WICKET"
26
27class ExtraType(Enum):
28 WIDE = "WIDE"
29 NO_BALL = "NO_BALL"
30 BYE = "BYE"
31 LEG_BYE = "LEG_BYE"These enums define fixed domain values such as match format, player roles, and ball outcomes. They ensure consistency and simplify condition checks.
Tracks stats for each player per match.
1class PlayerStats:
2 def __init__(self):
3 self.runs = 0
4 self.balls_played = 0
5 self.wickets = 0
6
7 def update_runs(self, run_scored):
8 self.runs += run_scored
9
10 def increment_balls_played(self):
11 self.balls_played += 1
12
13 def increment_wickets(self):
14 self.wickets += 1
15
16 def get_runs(self):
17 return self.runs
18
19 def get_balls_played(self):
20 return self.balls_played
21
22 def get_wickets(self):
23 return self.wickets
24
25 def __str__(self):
26 return f"Runs: {self.runs}, Balls Played: {self.balls_played}, Wickets: {self.wickets}"Stats are mutable and updated on every ball.
1class Player:
2 def __init__(self, player_id, name, role):
3 self.id = player_id
4 self.name = name
5 self.role = role
6 self.stats = PlayerStats()
7
8 def get_id(self):
9 return self.id
10
11 def get_name(self):
12 return self.name
13
14 def get_role(self):
15 return self.role
16
17 def get_stats(self):
18 return self.stats1class Team:
2 def __init__(self, team_id, name, players):
3 self.id = team_id
4 self.name = name
5 self.players = players
6
7 def get_id(self):
8 return self.id
9
10 def get_name(self):
11 return self.name
12
13 def get_players(self):
14 return self.playersPlayer has a unique ID, role, and in-memory stat object.Team encapsulates players and their name/identity.A flexible design for constructing different types of wicket events using the Builder Pattern, allowing optional fields for context-specific dismissals.
1class Wicket:
2 class Builder:
3 def __init__(self, wicket_type, player_out):
4 self.wicket_type = wicket_type
5 self.player_out = player_out
6 self.caught_by = None
7 self.runout_by = None
8
9 def caught_by(self, player):
10 self.caught_by = player
11 return self
12
13 def runout_by(self, player):
14 self.runout_by = player
15 return self
16
17 def build(self):
18 return Wicket(self)
19
20 def __init__(self, builder):
21 self.wicket_type = builder.wicket_type
22 self.player_out = builder.player_out
23 self.caught_by = builder.caught_by
24 self.runout_by = builder.runout_by
25
26 def get_wicket_type(self):
27 return self.wicket_type
28
29 def get_player_out(self):
30 return self.player_out
31
32 def get_caught_by(self):
33 return self.caught_by
34
35 def get_runout_by(self):
36 return self.runout_byEach Ball captures one legal delivery (or extra) with its result.
1class Ball:
2 class BallBuilder:
3 def __init__(self):
4 self.ball_number = 0
5 self.bowled_by = None
6 self.faced_by = None
7 self.runs_scored = 0
8 self.wicket = None
9 self.extra_type = None
10 self.commentary = None
11
12 def with_ball_number(self, ball_number):
13 self.ball_number = ball_number
14 return self
15
16 def bowled_by(self, bowler):
17 self.bowled_by = bowler
18 return self
19
20 def faced_by(self, batsman):
21 self.faced_by = batsman
22 return self
23
24 def with_runs(self, runs):
25 self.runs_scored = runs
26 return self
27
28 def with_wicket(self, wicket):
29 self.wicket = wicket
30 return self
31
32 def with_extra_type(self, extra):
33 self.extra_type = extra
34 return self
35
36 def with_commentary(self, commentary):
37 self.commentary = commentary
38 return self
39
40 def build(self):
41 temp_ball = Ball(self)
42
43 if self.commentary is None:
44 self.commentary = CommentaryManager.get_instance().generate_commentary(temp_ball)
45
46 return Ball(self)
47
48 def __init__(self, builder):
49 self.ball_number = builder.ball_number
50 self.bowled_by = builder.bowled_by
51 self.faced_by = builder.faced_by
52 self.runs_scored = builder.runs_scored
53 self.wicket = builder.wicket
54 self.extra_type = builder.extra_type
55 self.commentary = builder.commentary
56
57 def is_wicket(self):
58 return self.wicket is not None
59
60 def is_boundary(self):
61 return self.runs_scored == 4 or self.runs_scored == 6
62
63 def get_ball_number(self):
64 return self.ball_number
65
66 def get_bowled_by(self):
67 return self.bowled_by
68
69 def get_faced_by(self):
70 return self.faced_by
71
72 def get_runs_scored(self):
73 return self.runs_scored
74
75 def get_wicket(self):
76 return self.wicket
77
78 def get_extra_type(self):
79 return self.extra_type
80
81 def get_commentary(self):
82 return self.commentaryCommentary is dynamically generated using the CommentaryManager.
Manages team performance and ball-by-ball progression.
1class Innings:
2 def __init__(self, batting_team, bowling_team):
3 self.batting_team = batting_team
4 self.bowling_team = bowling_team
5 self.score = 0
6 self.wickets = 0
7 self.balls = []
8 self.player_stats = {}
9
10 for player in batting_team.get_players():
11 self.player_stats[player] = PlayerStats()
12 for player in bowling_team.get_players():
13 self.player_stats[player] = PlayerStats()
14
15 def add_ball(self, ball):
16 self.balls.append(ball)
17 runs_scored = ball.get_runs_scored()
18 self.score += runs_scored
19
20 if ball.get_extra_type() in [ExtraType.WIDE, ExtraType.NO_BALL]:
21 self.score += 1
22 else:
23 ball.get_faced_by().get_stats().update_runs(runs_scored)
24 ball.get_faced_by().get_stats().increment_balls_played()
25 self.player_stats[ball.get_faced_by()].update_runs(runs_scored)
26 self.player_stats[ball.get_faced_by()].increment_balls_played()
27
28 if ball.is_wicket():
29 self.wickets += 1
30 ball.get_bowled_by().get_stats().increment_wickets()
31 self.player_stats[ball.get_bowled_by()].increment_wickets()
32
33 def print_player_stats(self):
34 for player, stats in self.player_stats.items():
35 if stats.get_balls_played() > 0 or stats.get_wickets() > 0:
36 print(f"Player: {player.get_name()} - Stats: {stats}")
37
38 def get_overs(self):
39 valid_balls = sum(1 for b in self.balls
40 if b.get_extra_type() not in [ExtraType.WIDE, ExtraType.NO_BALL])
41
42 completed_overs = valid_balls // 6
43 balls_in_current_over = valid_balls % 6
44
45 return completed_overs + (balls_in_current_over / 10.0)
46
47 def get_batting_team(self):
48 return self.batting_team
49
50 def get_bowling_team(self):
51 return self.bowling_team
52
53 def get_score(self):
54 return self.score
55
56 def get_wickets(self):
57 return self.wickets
58
59 def get_balls(self):
60 return self.ballsUpdates both player and team-level statistics.
1class Match:
2 def __init__(self, match_id, team1, team2, format_strategy):
3 self.id = match_id
4 self.team1 = team1
5 self.team2 = team2
6 self.format_strategy = format_strategy
7 self.innings = [Innings(team1, team2)]
8 self.current_state = ScheduledState()
9 self.current_status = None
10 self.observers = []
11 self.winner = None
12 self.result_message = ""
13
14 def process_ball(self, ball):
15 self.current_state.process_ball(self, ball)
16
17 def start_next_innings(self):
18 self.current_state.start_next_innings(self)
19
20 def create_new_innings(self):
21 if len(self.innings) >= self.format_strategy.get_total_innings():
22 print("Cannot create a new innings, match has already reached its limit.")
23 return
24
25 next_innings = Innings(self.team2, self.team1)
26 self.innings.append(next_innings)
27
28 def add_observer(self, observer):
29 self.observers.append(observer)
30
31 def remove_observer(self, observer):
32 self.observers.remove(observer)
33
34 def notify_observers(self, ball):
35 for observer in self.observers:
36 observer.update(self, ball)
37
38 def get_current_innings(self):
39 return self.innings[-1]
40
41 def get_id(self):
42 return self.id
43
44 def get_team1(self):
45 return self.team1
46
47 def get_team2(self):
48 return self.team2
49
50 def get_format_strategy(self):
51 return self.format_strategy
52
53 def get_innings(self):
54 return self.innings
55
56 def get_current_status(self):
57 return self.current_status
58
59 def get_winner(self):
60 return self.winner
61
62 def get_result_message(self):
63 return self.result_message
64
65 def set_state(self, state):
66 self.current_state = state
67
68 def set_current_status(self, status):
69 self.current_status = status
70
71 def set_winner(self, winner):
72 self.winner = winner
73
74 def set_result_message(self, message):
75 self.result_message = messageThe core engine of a cricket match:
Provides flexibility to support multiple match formats like T20, ODI using the Strategy Pattern.
1class MatchFormatStrategy(ABC):
2 @abstractmethod
3 def get_total_innings(self):
4 pass
5
6 @abstractmethod
7 def get_total_overs(self):
8 pass
9
10 @abstractmethod
11 def get_format_name(self):
12 pass
13
14class T20FormatStrategy(MatchFormatStrategy):
15 def get_total_innings(self):
16 return 2
17
18 def get_total_overs(self):
19 return 20
20
21 def get_format_name(self):
22 return "T20"
23
24class ODIFormatStrategy(MatchFormatStrategy):
25 def get_total_innings(self):
26 return 2
27
28 def get_total_overs(self):
29 return 50
30
31 def get_format_name(self):
32 return "ODI"Each observer responds differently to match updates.
1class MatchObserver(ABC):
2 @abstractmethod
3 def update(self, match, last_ball):
4 pass
5
6class CommentaryDisplay(MatchObserver):
7 def update(self, match, last_ball):
8 if match.get_current_status() == MatchStatus.FINISHED:
9 print("[COMMENTARY]: Match has finished!")
10 elif match.get_current_status() == MatchStatus.IN_BREAK:
11 print("[COMMENTARY]: Inning has ended!")
12 else:
13 print(f"[COMMENTARY]: {last_ball.get_commentary()}")
14
15class UserNotifier(MatchObserver):
16 def update(self, match, last_ball):
17 if match.get_current_status() == MatchStatus.FINISHED:
18 print("[NOTIFICATION]: Match has finished!")
19 elif match.get_current_status() == MatchStatus.IN_BREAK:
20 print("[NOTIFICATION]: Inning has ended!")
21 elif last_ball and last_ball.is_wicket():
22 print("[NOTIFICATION]: Wicket! A player is out.")
23 elif last_ball and last_ball.is_boundary():
24 print(f"[NOTIFICATION]: It's a boundary! {last_ball.get_runs_scored()} runs.")
25
26class ScorecardDisplay(MatchObserver):
27 def update(self, match, last_ball):
28 if match.get_current_status() == MatchStatus.FINISHED:
29 print("\n--- MATCH RESULT ---")
30 print(match.get_result_message().upper())
31 print("--------------------")
32
33 print("Player Stats:")
34 counter = 1
35 for inning in match.get_innings():
36 print(f"Inning {counter}")
37 inning.print_player_stats()
38 counter += 1
39 elif match.get_current_status() == MatchStatus.IN_BREAK:
40 print("\n--- END OF INNINGS ---")
41 last_innings = match.get_innings()[-1]
42 print(f"Final Score: {last_innings.get_batting_team().get_name()}: "
43 f"{last_innings.get_score()}/{last_innings.get_wickets()} "
44 f"(Overs: {last_innings.get_overs():.1f})")
45 print("------------------------")
46 else:
47 print("\n--- SCORECARD UPDATE ---")
48 current_innings = match.get_current_innings()
49 print(f"{current_innings.get_batting_team().get_name()}: "
50 f"{current_innings.get_score()}/{current_innings.get_wickets()} "
51 f"(Overs: {current_innings.get_overs():.1f})")
52 print("------------------------")In-memory storage for matches and players. Simulates persistence.
1class MatchRepository:
2 def __init__(self):
3 self.matches = {}
4
5 def save(self, match):
6 self.matches[match.get_id()] = match
7
8 def find_by_id(self, match_id):
9 return self.matches.get(match_id)
10
11class PlayerRepository:
12 def __init__(self):
13 self.players = {}
14
15 def save(self, player):
16 self.players[player.get_id()] = player
17
18 def find_by_id(self, player_id):
19 return self.players.get(player_id)Manages match flow using State Pattern.
1class MatchState(ABC):
2 @abstractmethod
3 def process_ball(self, match, ball):
4 pass
5
6 def start_next_innings(self, match):
7 print("ERROR: Cannot start the next innings from the current state.")
8
9class ScheduledState(MatchState):
10 def process_ball(self, match, ball):
11 print("ERROR: Cannot process a ball for a match that has not started.")
12
13class InBreakState(MatchState):
14 def process_ball(self, match, ball):
15 print("ERROR: Cannot process a ball. The match is currently in a break.")
16
17 def start_next_innings(self, match):
18 print("Starting the next innings...")
19 match.create_new_innings()
20 match.set_state(LiveState())
21 match.set_current_status(MatchStatus.LIVE)
22
23class FinishedState(MatchState):
24 def process_ball(self, match, ball):
25 print("ERROR: Cannot process a ball for a finished match.")
26
27class LiveState(MatchState):
28 def process_ball(self, match, ball):
29 current_innings = match.get_current_innings()
30 current_innings.add_ball(ball)
31 match.notify_observers(ball)
32 self.check_for_match_end(match)
33
34 def check_for_match_end(self, match):
35 current_innings = match.get_current_innings()
36 innings_count = len(match.get_innings())
37 is_final_innings = (innings_count == match.get_format_strategy().get_total_innings())
38
39 # Win condition: Chasing team surpasses the target
40 if is_final_innings:
41 target_score = match.get_innings()[0].get_score() + 1
42 if current_innings.get_score() >= target_score:
43 wickets_remaining = (len(current_innings.get_batting_team().get_players()) - 1) - current_innings.get_wickets()
44 self.declare_winner(match, current_innings.get_batting_team(), f"won by {wickets_remaining} wickets")
45 return
46
47 # End of innings condition
48 if self.is_innings_over(match):
49 if is_final_innings:
50 score1 = match.get_innings()[0].get_score()
51 score2 = current_innings.get_score()
52
53 if score1 > score2:
54 self.declare_winner(match, match.get_team1(), f"won by {score1 - score2} runs")
55 elif score2 > score1:
56 wickets_remaining = (len(current_innings.get_batting_team().get_players()) - 1) - current_innings.get_wickets()
57 self.declare_winner(match, current_innings.get_batting_team(), f"won by {wickets_remaining} wickets")
58 else:
59 self.declare_winner(match, None, "Match Tied")
60 else:
61 print("End of the innings!")
62 match.set_state(InBreakState())
63 match.set_current_status(MatchStatus.IN_BREAK)
64 match.notify_observers(None)
65
66 def declare_winner(self, match, winning_team, message):
67 print("MATCH FINISHED!")
68 match.set_winner(winning_team)
69 result_message = f"{winning_team.get_name()} {message}" if winning_team else message
70 match.set_result_message(result_message)
71
72 match.set_state(FinishedState())
73 match.set_current_status(MatchStatus.FINISHED)
74 match.notify_observers(None)
75
76 def is_innings_over(self, match):
77 current_innings = match.get_current_innings()
78 all_out = current_innings.get_wickets() >= len(current_innings.get_batting_team().get_players()) - 1
79 overs_finished = int(current_innings.get_overs()) >= match.get_format_strategy().get_total_overs()
80 return all_out or overs_finishedTransitions include:
Dynamically generates commentary using event-specific templates. Centralized via Singleton Pattern and extensible through a template engine.
1class CommentaryManager:
2 _instance = None
3
4 def __init__(self):
5 self.random = random.Random()
6 self.commentary_templates = {}
7 self.initialize_templates()
8
9 @classmethod
10 def get_instance(cls):
11 if cls._instance is None:
12 cls._instance = CommentaryManager()
13 return cls._instance
14
15 def initialize_templates(self):
16 self.commentary_templates["RUNS_0"] = [
17 "%s defends solidly.",
18 "No run, good fielding by the cover fielder.",
19 "A dot ball to end the over.",
20 "Pushed to mid-on, but no run."
21 ]
22 self.commentary_templates["RUNS_1"] = [
23 "Tucked away to the leg side for a single.",
24 "Quick single taken by %s.",
25 "Pushed to long-on for one."
26 ]
27 self.commentary_templates["RUNS_2"] = [
28 "Two runs taken!",
29 "Quick double taken by %s.",
30 "Pushed to mid-on for two."
31 ]
32 self.commentary_templates["RUNS_4"] = [
33 "FOUR! %s smashes it through the covers!",
34 "Beautiful shot! That's a boundary.",
35 "Finds the gap perfectly. Four runs."
36 ]
37 self.commentary_templates["RUNS_6"] = [
38 "SIX! That's out of the park!",
39 "%s sends it sailing over the ropes!",
40 "Massive hit! It's a maximum."
41 ]
42
43 self.commentary_templates[f"WICKET_{WicketType.BOWLED.value}"] = [
44 "BOWLED HIM! %s misses completely and the stumps are shattered!",
45 "Cleaned up! A perfect yorker from %s."
46 ]
47 self.commentary_templates[f"WICKET_{WicketType.CAUGHT.value}"] = [
48 "CAUGHT! %s skies it and the fielder takes a comfortable catch.",
49 "Out! A brilliant catch in the deep by %s."
50 ]
51 self.commentary_templates[f"WICKET_{WicketType.LBW.value}"] = [
52 "LBW! That one kept low and struck %s right in front.",
53 "%s completely misjudged the line and pays the price."
54 ]
55 self.commentary_templates[f"WICKET_{WicketType.STUMPED.value}"] = [
56 "STUMPED! %s misses it, and the keeper does the rest!",
57 "Gone! Lightning-fast work by the keeper to stump %s."
58 ]
59
60 self.commentary_templates[f"EXTRA_{ExtraType.WIDE.value}"] = [
61 "That's a wide. The umpire signals an extra run.",
62 "Too far down the leg side, that'll be a wide."
63 ]
64 self.commentary_templates[f"EXTRA_{ExtraType.NO_BALL.value}"] = [
65 "No ball! %s has overstepped. It's a free hit.",
66 "It's a no-ball for overstepping."
67 ]
68
69 def generate_commentary(self, ball):
70 key = self.get_event_key(ball)
71 templates = self.commentary_templates.get(key, ["Just a standard delivery."])
72
73 template = self.random.choice(templates)
74
75 batsman_name = ball.get_faced_by().get_name() if ball.get_faced_by() else ""
76 bowler_name = ball.get_bowled_by().get_name() if ball.get_bowled_by() else ""
77
78 try:
79 return template % batsman_name
80 except:
81 return template.replace("%s", batsman_name)
82
83 def get_event_key(self, ball):
84 if ball.is_wicket():
85 return f"WICKET_{ball.get_wicket().get_wicket_type().value}"
86 if ball.get_extra_type():
87 return f"EXTRA_{ball.get_extra_type().value}"
88 if 0 <= ball.get_runs_scored() <= 6:
89 return f"RUNS_{ball.get_runs_scored()}"
90 return "DEFAULT"The system uses centralized services and a facade to manage the overall application flow.
1class CricInfoService:
2 _instance = None
3
4 def __init__(self):
5 self.match_repository = MatchRepository()
6 self.player_repository = PlayerRepository()
7
8 @classmethod
9 def get_instance(cls):
10 if cls._instance is None:
11 cls._instance = CricInfoService()
12 return cls._instance
13
14 def create_match(self, team1, team2, format_strategy):
15 match_id = str(uuid.uuid4())
16 match = Match(match_id, team1, team2, format_strategy)
17 self.match_repository.save(match)
18 print(f"Match {format_strategy.get_format_name()} created between {team1.get_name()} and {team2.get_name()}.")
19 return match
20
21 def start_match(self, match_id):
22 match = self.match_repository.find_by_id(match_id)
23 if match:
24 match.set_state(LiveState())
25 print(f"Match {match_id} is now LIVE.")
26
27 def process_ball_update(self, match_id, ball):
28 match = self.match_repository.find_by_id(match_id)
29 if match:
30 match.process_ball(ball)
31
32 def start_next_innings(self, match_id):
33 match = self.match_repository.find_by_id(match_id)
34 if match:
35 match.start_next_innings()
36
37 def subscribe_to_match(self, match_id, observer):
38 match = self.match_repository.find_by_id(match_id)
39 if match:
40 match.add_observer(observer)
41
42 def end_match(self, match_id):
43 match = self.match_repository.find_by_id(match_id)
44 if match:
45 match.set_state(FinishedState())
46 print(f"Match {match_id} has FINISHED.")
47
48 def add_player(self, player_id, player_name, player_role):
49 player = Player(player_id, player_name, player_role)
50 self.player_repository.save(player)
51 return playerThe demo class validates the end-to-end functionality by simulating a cricket match.
1class CricinfoDemo:
2 @staticmethod
3 def main():
4 service = CricInfoService.get_instance()
5
6 # Setup Players and Teams
7 p1 = service.add_player("P1", "Virat", PlayerRole.BATSMAN)
8 p2 = service.add_player("P2", "Rohit", PlayerRole.BATSMAN)
9 p3 = service.add_player("P3", "Bumrah", PlayerRole.BOWLER)
10 p4 = service.add_player("P4", "Jadeja", PlayerRole.ALL_ROUNDER)
11
12 p5 = service.add_player("P5", "Warner", PlayerRole.BATSMAN)
13 p6 = service.add_player("P6", "Smith", PlayerRole.BATSMAN)
14 p7 = service.add_player("P7", "Starc", PlayerRole.BOWLER)
15 p8 = service.add_player("P8", "Maxwell", PlayerRole.ALL_ROUNDER)
16
17 india = Team("T1", "India", [p1, p2, p3, p4])
18 australia = Team("T2", "Australia", [p5, p6, p7, p8])
19
20 # Create a T20 Match
21 t20_match = service.create_match(india, australia, T20FormatStrategy())
22 match_id = t20_match.get_id()
23
24 # Create and subscribe observers
25 scorecard = ScorecardDisplay()
26 commentary = CommentaryDisplay()
27 notifier = UserNotifier()
28
29 service.subscribe_to_match(match_id, scorecard)
30 service.subscribe_to_match(match_id, commentary)
31 service.subscribe_to_match(match_id, notifier)
32
33 # Start the match
34 service.start_match(match_id)
35
36 print("\n--- SIMULATING FIRST INNINGS ---")
37 service.process_ball_update(match_id, Ball.BallBuilder()
38 .bowled_by(p7).faced_by(p1).with_runs(2).build())
39 service.process_ball_update(match_id, Ball.BallBuilder()
40 .bowled_by(p7).faced_by(p1).with_runs(1).build())
41 service.process_ball_update(match_id, Ball.BallBuilder()
42 .bowled_by(p7).faced_by(p2).with_runs(6).build())
43
44 p2_wicket = Wicket.Builder(WicketType.BOWLED, p2).build()
45 service.process_ball_update(match_id, Ball.BallBuilder()
46 .bowled_by(p7).faced_by(p2).with_runs(0).with_wicket(p2_wicket).build())
47
48 p3_wicket = Wicket.Builder(WicketType.LBW, p3).build()
49 service.process_ball_update(match_id, Ball.BallBuilder()
50 .bowled_by(p7).faced_by(p3).with_runs(0).with_wicket(p3_wicket).build())
51
52 service.process_ball_update(match_id, Ball.BallBuilder()
53 .bowled_by(p7).faced_by(p4).with_runs(4).build())
54
55 p4_wicket = Wicket.Builder(WicketType.CAUGHT, p4).caught_by(p6).build()
56 service.process_ball_update(match_id, Ball.BallBuilder()
57 .bowled_by(p7).faced_by(p4).with_runs(0).with_wicket(p4_wicket).build())
58
59 print("\n\n--- INNINGS BREAK ---")
60 print("Players are off the field. Preparing for the second innings.")
61
62 # Start the second innings
63 service.start_next_innings(match_id)
64
65 print("\n--- SIMULATING SECOND INNINGS ---")
66 service.process_ball_update(match_id, Ball.BallBuilder()
67 .bowled_by(p3).faced_by(p5).with_runs(4).build())
68
69 service.process_ball_update(match_id, Ball.BallBuilder()
70 .bowled_by(p3).faced_by(p5).with_runs(1).build())
71
72 p5_wicket = Wicket.Builder(WicketType.BOWLED, p5).build()
73 service.process_ball_update(match_id, Ball.BallBuilder()
74 .bowled_by(p3).faced_by(p5).with_runs(0).with_wicket(p5_wicket).build())
75
76 p7_wicket = Wicket.Builder(WicketType.LBW, p7).build()
77 service.process_ball_update(match_id, Ball.BallBuilder()
78 .bowled_by(p3).faced_by(p7).with_runs(0).with_wicket(p7_wicket).build())
79
80 p8_wicket = Wicket.Builder(WicketType.STUMPED, p8).build()
81 service.process_ball_update(match_id, Ball.BallBuilder()
82 .bowled_by(p3).faced_by(p8).with_runs(0).with_wicket(p8_wicket).build())
83
84 service.end_match(match_id)
85
86if __name__ == "__main__":
87 CricinfoDemo.main()Which entity is primarily responsible for representing a single delivery in a cricket match?
No comments yet. Be the first to comment!